package cadtoolbox.graphical;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowEvent;
import java.awt.event.WindowListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.concurrent.ExecutionException;

import javax.swing.JFileChooser;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JProgressBar;
import javax.swing.ProgressMonitor;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.MutableTreeNode;

import cadtoolbox.optimizer.Parameter;

import cadtoolbox.utils.CodeGenerator;
import cadtoolbox.utils.MyWorker;
import cadtoolbox.utils.PlotExpData;

import cadtoolbox.model.Constants;
import cadtoolbox.model.ExternalInput;
import cadtoolbox.model.OligoGraph;
import cadtoolbox.model.OligoSystem;
import cadtoolbox.model.SequenceVertex;

import edu.uci.ics.jung.algorithms.layout.Layout;


public class MyActionlistener <V,E> implements ActionListener {

	private OligoGraph<V,E> graph;
	private Layout<V,E> layout;
	private DataPanel<E> data;
	private OligoSystem<E> model;
	private OptionsFrameAdvanced optionFrame = null;
	
	JFileChooser fc = new JFileChooser();
	 FileNameExtensionFilter filter = new FileNameExtensionFilter(
		        "Graph files", "graph");
	File savingFile;
	//FileNameExtensionFilter exportsbmlfilter = new FileNameExtensionFilter( "SBML files", "sbml");
	//FileNameExtensionFilter exportMathematicafilter = new FileNameExtensionFilter( "Mathematica files", "");
	public MyActionlistener(Layout<V,E> layout,DataPanel<E> data){
		this.layout = layout;
		this.graph= (OligoGraph<V,E>) layout.getGraph();
		this.data = data;
		this.model = new OligoSystem(this.graph);
		fc.setFileFilter(filter);
	}
	
	@Override
	public void actionPerformed(ActionEvent arg0) {
		if (arg0.getActionCommand() == "new"){
			graph.totalReset();
			Constants.reset();
			data.update();
			data.getParent().repaint();
		} else if (arg0.getActionCommand() == "newseq"){
			newseq();
			
		} else if (arg0.getActionCommand() == "remove"){
			removevertex();
			
		} else if (arg0.getActionCommand() == "duplicate"){
			
			boolean rep = PlotExpData.autoplot;
			PlotExpData.autoplot = false;
			duplicate();
			PlotExpData.autoplot = rep;
			if(rep)
				graph.replot();
			
		} else if (arg0.getActionCommand() == "fusion"){
			boolean rep = PlotExpData.autoplot;
			
			PlotExpData.autoplot = false;
			
			fusion();
			PlotExpData.autoplot = rep;
			if(rep)
				graph.replot();
			
		} else if (arg0.getActionCommand() == "save"){
			if (savingFile == null){
				
				int result = fc.showSaveDialog(null);
				if (result == JFileChooser.APPROVE_OPTION){
					this.savingFile = fc.getSelectedFile();
					performSave();
				}
			} else {
				performSave();
			}
		} else if (arg0.getActionCommand() == "saveas"){
			int result = fc.showSaveDialog(null);
			if (result == JFileChooser.APPROVE_OPTION){
				this.savingFile = fc.getSelectedFile();
				performSave();
			}
		} else if (arg0.getActionCommand() == "open"){
			boolean rep = PlotExpData.autoplot;
			PlotExpData.autoplot = false;
			open();
			PlotExpData.autoplot = rep;
			if(rep)
				graph.replot();
		} else if (arg0.getActionCommand() == "import"){
			boolean rep = PlotExpData.autoplot;
			PlotExpData.autoplot = false;
			doimport();
			if(rep)
				graph.replot();
		} else if (arg0.getActionCommand() == "impInput"){
			doinput();
		} else if (arg0.getActionCommand() == "export"){
			Object[] options = {"SBML",
	                "Mathematica",
	                "Plot only"};
			int request = JOptionPane.showOptionDialog(null, "Export system","Format", JOptionPane.YES_NO_CANCEL_OPTION,
				    JOptionPane.QUESTION_MESSAGE,
				    null,
				    options,
				    options[0]);
			if (request == 0) {
				export();
			} else if (request == 1){
				exportMathematica();
			} else if (request == 2){
				exportTimeSerie();
				
			}
		} else if (arg0.getActionCommand() == "plot"){
				plot();
				
		} else if (arg0.getActionCommand() == "options"){
			if(this.optionFrame==null){
			 this.optionFrame = new OptionsFrameAdvanced<V,E>(graph);
			 this.optionFrame.addWindowListener(new WindowListener(){

				@Override
				public void windowActivated(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowClosed(WindowEvent arg0) {
					optionFrameClosed();
					
				}

				@Override
				public void windowClosing(WindowEvent arg0) {
					// TODO Auto-generated method stub
					if(PlotExpData.autoplot)
						graph.replot();
					optionFrameClosed();
				}

				@Override
				public void windowDeactivated(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowDeiconified(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowIconified(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}

				@Override
				public void windowOpened(WindowEvent arg0) {
					// TODO Auto-generated method stub
					
				}
				 
			 });
			this.optionFrame.setVisible(true);
			}
		} else if (arg0.getActionCommand() == "undo"){
			undo();
		}
	}

	private void optionFrameClosed(){
		this.optionFrame = null;
	}
	
	private void undo() {
		graph.undo();
		data.update();
		data.getParent().repaint();
	}

	private void exportMathematica() {
		// TODO Auto-generated method stub
		JFileChooser exp = new JFileChooser();
		int result = exp.showSaveDialog(null);
		if (result == JFileChooser.APPROVE_OPTION){
			File selectedFile = exp.getSelectedFile();
			try {
				if (!selectedFile.exists()){

					selectedFile.createNewFile();
				
				}
				
				FileWriter fw = new FileWriter(selectedFile);
				//TODO: this HAS to change
				//fw.write(CodeGenerator.exportToMathematicaCode((OligoGraph<SequenceVertex,E>)graph));
				fw.write(CodeGenerator.exportToMathematicaCode((OligoGraph<SequenceVertex,E>) graph));
				fw.flush();
				fw.close();
				
			} catch (IOException e) {
				
				e.printStackTrace();
			}
		}
	}

	private void exportTimeSerie() {
		// TODO Auto-generated method stub
		model = new OligoSystem(this.graph);
		double[][] res = model.calculateTimeSeries(null);
		String export = "#";
		for(int i=0;i<this.graph.getVertexCount();i++){
			export += "s"+i+"\t";
		}
		if(Constants.fullExport){
		Iterator<E> it = model.templates.keySet().iterator();
		while(it.hasNext()){
			E e = it.next();
			export += ""+e+"alone"+"\t"+e+"in"+"\t"+e+"out"+"\t"+e+"both"+"\t"+e+"ext"+"\t"+e+"inhibited"+"\t";
		}
		}
		export += "\n";
		
		for(int t=0;t<res[0].length;t++){
			for(int i=0;i<(Constants.fullExport?res.length:graph.getVertexCount());i++){
				export+= ""+ res[i][t] + "\t";
			}
			export+= "\n";
		}
		JFileChooser exp = new JFileChooser();
		int result = exp.showSaveDialog(null);
		if (result == JFileChooser.APPROVE_OPTION){
			File selectedFile = exp.getSelectedFile();
			try {
				if (!selectedFile.exists()){

					selectedFile.createNewFile();
				
				}
				
				FileWriter fw = new FileWriter(selectedFile);
				fw.write(export);
				fw.flush();
				fw.close();
				
			} catch (IOException e) {
				
				e.printStackTrace();
			}
		}
	}

	private void newseq(){
		
		Object[] options = {"Activation sequence",
                "Inhibiting sequence",
                "Cancel"};
		int request = JOptionPane.showOptionDialog(null, "Create a sequence?","New sequence", JOptionPane.YES_NO_CANCEL_OPTION,
			    JOptionPane.QUESTION_MESSAGE,
			    null,
			    options,
			    options[0]);
		V newvertex = null;
		if (request == 0){
			newvertex = graph.getVertexFactory().create();
			graph.addVertex(newvertex);
			graph.K.put(newvertex, 1/Constants.PadiracKSimpleDiv);
			data.update();
			data.getParent().repaint();
		} else if (request == 1){
			if (graph.getEdges().isEmpty()){
				JOptionPane.showMessageDialog(null, "No template yet, create the inhibited template first");
				return;
			} else {
				if(!graph.getInhibitableTemplates().isEmpty()){
					E inhibtemp = (E) JOptionPane.showInputDialog(null, "Inhibited template?", "New inhibiting sequence", JOptionPane.PLAIN_MESSAGE, null, graph.getInhibitableTemplates().toArray(), (Object) graph.getEdges().iterator().next());
					if (inhibtemp != null){
						newvertex = graph.getVertexFactory().create();
						((SequenceVertex)newvertex).setInhib(true);
						graph.addVertex(newvertex);
						graph.K.put(newvertex, 1/Constants.PadiracKInhibDiv);
						graph.addInhibition(inhibtemp, newvertex);
						data.update();
						data.getParent().repaint();
		    		}
				} else {
					JOptionPane.showMessageDialog(null, "No available template");
				}
			}
		}
		if(newvertex != null){
			Parameter tempSeqK = null;
			//seqK
			Enumeration<DefaultMutableTreeNode> it = ((MutableTreeNode) graph.optimizable.getChild(graph.optimizable.getRoot(), 0)).children();
			while(it.hasMoreElements()){
				DefaultMutableTreeNode next = it.nextElement();
				if(((Parameter) next.getUserObject()).target.equals(newvertex)){
					tempSeqK = (Parameter) next.getUserObject();
					break;
				}
			}
			if(tempSeqK != null){
				tempSeqK.currentValue = graph.getK(newvertex);
				tempSeqK.minValue = graph.isInhibitor(newvertex)? Constants.inhibKmin : Constants.simpleKmin;
				tempSeqK.maxValue = graph.isInhibitor(newvertex)? Constants.inhibKmax : Constants.simpleKmax;
			}
			//seqC
			it = ((MutableTreeNode) graph.optimizable.getChild(graph.optimizable.getRoot(), 1)).children();
			while(it.hasMoreElements()){
				DefaultMutableTreeNode next = it.nextElement();
				if(((Parameter) next.getUserObject()).target.equals(newvertex)){
					tempSeqK = (Parameter) next.getUserObject();
					break;
				}
			}
			if(tempSeqK != null){
				tempSeqK.currentValue = ((SequenceVertex) newvertex).initialConcentration;
				tempSeqK.minValue = 0;
				tempSeqK.maxValue = 100;
			}
		}
	}

	private void removevertex(){
		for (V i: graph.getSelected()){
			if(graph.getVertices().contains(i))
				graph.removeVertex(i);
			 
		 }
		graph.getSelected().clear();
		 data.update();
		 data.getParent().repaint();
	}
	
	private void duplicate(){
		ArrayList<V> notrealselected = getNotRealSelected();
		HashMap<V,V> translation = getNewNamesForVertices(notrealselected);
		
		for(V i: graph.getSelected()){
			
			addEdgesForVertex(i,translation);
			updateInhibitions(translation);
		
		}
		
		if(graph.getSelected().size()!=0){
			data.update();
			data.getParent().repaint();
		}
	}
	
	private ArrayList<V> getNotRealSelected(){
		ArrayList<V> notrealselected = new ArrayList<V>();
		V from;
		for (E e: graph.getInhibitions()){
			from = graph.getInhibition(e).getLeft();
			if (graph.getSelected().contains(from) && !(graph.getSelected().containsAll(graph.getEndpoints(e)))){
				notrealselected.add(from);
			}
		}
		return notrealselected;
	}
	
	private HashMap<V,V> getNewNamesForVertices(ArrayList<V> notrealselected){
		HashMap<V,V> translation = new HashMap<V,V>();
		V newvertex;
		for (V i : graph.getSelected()){
			if (notrealselected.contains(i)){
				translation.put(i, i);
			} else {
				newvertex = graph.getVertexFactory().create();
			translation.put(i, newvertex)  ;
			graph.addVertex(newvertex); 
			}
		}
		return translation;
	}
	
	private void addEdgesForVertex(V i, HashMap<V,V> translation){
		V from,to;
		for (E e: graph.getOutEdges(i)){
			from = graph.getEndpoints(e).getFirst();
			to = graph.getEndpoints(e).getSecond();
			if(graph.getSelected().contains(from)&&graph.getSelected().contains(to)){
				graph.addActivation(graph.getEdgeFactory().createEdge(translation.get(from), translation.get(to)), translation.get(from), translation.get(to));
			}
		}
	}
	
	private void updateInhibitions(HashMap<V,V> translation){
		V from, to;
		ArrayList<E> inhibs = new ArrayList<E>(graph.getInhibitions());
		
		for (E e: inhibs){
			if(graph.getSelected().contains(graph.getInhibition(e).getLeft())){
			from = graph.getEndpoints(e).getFirst();
			to = graph.getEndpoints(e).getSecond();
			
			if(graph.getSelected().contains(from)&&graph.getSelected().contains(to)){
				graph.addInhibition(graph.getEdgeFactory().createEdge(translation.get(from), translation.get(to)), translation.get(graph.getInhibition(e).getLeft()));
			}
			}
		}
	}
	
	// TODO: bug if fusion with inhibitors
	private void fusion(){
		if(graph.getSelected().size()>1){
		V newnode = graph.getVertexFactory().create();
		V v2;
		graph.addVertex(newnode);
		graph.K.put(newnode, 1/Constants.PadiracKSimpleDiv);
		for (V v: graph.getSelected()){
			for(E e: graph.getInEdges(v)){
				v2 = graph.getEndpoints(e).getFirst();
				E newedge = graph.getEdgeFactory().createEdge(v2, newnode);
				if (!graph.getSelected().contains(v2) && !graph.containsEdge(newedge))
					graph.addActivation(newedge, v2, newnode);
			}
			for(E e: graph.getOutEdges(v)){
				v2 = graph.getEndpoints(e).getSecond();
				E newedge = graph.getEdgeFactory().createEdge(newnode, v2);
				if (!graph.getSelected().contains(v2) && ! graph.containsEdge(newedge))
					graph.addActivation(newedge, newnode, v2);
			}
		}
			removevertex();
			data.update();
			data.getParent().repaint();
		}
	}
	
	private void export(){
		JFileChooser exp = new JFileChooser();
		int result = exp.showSaveDialog(null);
		if (result == JFileChooser.APPROVE_OPTION){
			File selectedFile = exp.getSelectedFile();
			try {
				if (!selectedFile.exists()){

					selectedFile.createNewFile();
				
				}
				
				FileWriter fw = new FileWriter(selectedFile);
				//TODO: this HAS to change
				//fw.write(CodeGenerator.exportToMathematicaCode((OligoGraph<SequenceVertex,E>)graph));
				fw.write(CodeGenerator.exportToSBMLcode((OligoGraph<SequenceVertex,E>) graph));
				fw.flush();
				fw.close();
				
			} catch (IOException e) {
				
				e.printStackTrace();
			}
		}
	}
	
	private void performSave(){
		try {
			if (!savingFile.exists()){
				
				savingFile.createNewFile();
			
			}

			graph.saved = true;
			FileWriter fw = new FileWriter(savingFile);
			//TODO: this HAS to change
			fw.write(CodeGenerator.saveGraph((OligoGraph<SequenceVertex,E>)graph, (Layout<SequenceVertex, E>)layout));
			fw.flush();
			fw.close();

		} catch (IOException e) {
			
			e.printStackTrace();
		}
	}
	
	private void open(){
		
		int result = fc.showOpenDialog(null);
		if (result == JFileChooser.APPROVE_OPTION){
			File open = fc.getSelectedFile();
			CodeGenerator.openGraph((OligoGraph<SequenceVertex,E>)graph, (Layout<SequenceVertex,E>) layout,open);
		}
		data.update();
		data.getParent().repaint();
	}
	
private void doimport(){
		
		int result = fc.showOpenDialog(null);
		if (result == JFileChooser.APPROVE_OPTION){
			File open = fc.getSelectedFile();
			CodeGenerator.importGraph((OligoGraph<SequenceVertex,E>)graph,(Layout<SequenceVertex,E>) layout, open);
		}
		data.update();
		data.getParent().repaint();
	}

private void doinput(){
	if(graph.getVertexCount()>0){
		SequenceVertex seq = (SequenceVertex) JOptionPane.showInputDialog(null, "Sequence?", "Add input to sequence", JOptionPane.PLAIN_MESSAGE, null, graph.getVertices().toArray(), (SequenceVertex) graph.getVertices().iterator().next());
		if(seq != null){
			JFileChooser ffc = new JFileChooser();
			int result = ffc.showOpenDialog(null);
			if (result == JFileChooser.APPROVE_OPTION){
				File open = ffc.getSelectedFile();
				try {
					FileReader in = new FileReader(open);
					BufferedReader reader = new BufferedReader(in);
					String line = null;
					line = reader.readLine();
					String[] split;
					ArrayList<Double> values = new ArrayList<Double>();
					while ((line = reader.readLine()) != null){
						split= line.split("\t");
						for(int i=0; i<split.length;i++){
							values.add(Double.parseDouble(split[i]));
						}
					}
					Double[] inp = new Double[values.size()];
					inp = values.toArray(inp);
					seq.inputs.add(new ExternalInput(inp,open));
					graph.replot();
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}


private void plot() {
	
	model = new OligoSystem(this.graph);

	final MyWorker myWorker = new MyWorker(model,graph);
	
	ProgressMonitor progressMonitor = new ProgressMonitor(null,
            "Simulating",
            "", 0, 100);
	progressMonitor.setProgress(0);

	final ProgressMonitor cop = progressMonitor;
	
	myWorker.setProg(cop);
	myWorker.setCustomProgress(0);
	myWorker.addPropertyChangeListener(new PropertyChangeListener(){

		@Override
		public void propertyChange(PropertyChangeEvent evt) {
			//System.out.println(evt.getPropertyName());
			if ("progress".equals(evt.getPropertyName()) ) {
				//System.out.println("Balsdkbgn");
	            int progress = (Integer) evt.getNewValue();
	            cop.setProgress(progress);
			}
			
		}
		
	});
	
	myWorker.execute();
}
}
